﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Esb.Soa.Domain.ServiceHandlers;
using Esb.Domain.DomainObjects;
using Esb.Domain.Factories;

namespace Esb.Soa.Domain.Factories
{
    /// <summary>
    /// 服务调用类
    /// </summary>
    public class ServiceCall
    {
        /// <summary>
        /// 服务处理器
        /// </summary>
        public IServiceHandler ServiceHandler
        {
            get;
            set;
        }

        /// <summary>
        /// 请求方Id
        /// </summary>
        public int ConsumerId { 
            get; 
            set; 
        }

        /// <summary>
        /// 服务有关信息
        /// </summary>
        public ServiceDo Service
        {
            get;
            private set;
        }

        /// <summary>
        /// 是否已准备好调用
        /// </summary>
        private bool isReady
        {
            get;
            set;
        }

        /// <summary>
        /// 服务地址
        /// </summary>
        public string ServiceUrl 
        { 
            get;
            set; 
        }

        /// <summary>
        /// 架构
        /// </summary>
        public string Arch
        {
            get;
            set;
        }

        //TODO:服务信息是否提出来

        //public static List<ServiceDo> AllServices;
        //TODO:并发字典
        public static Dictionary<string, ServiceDo> AllServices;
        public static DateTime LastUpdateTime;
        public static bool IsFetchingDb;

        public string Method { get; set; }

        /// <summary>
        /// 初始化，获得服务信息
        /// </summary>
        /// <param name="serviceRoute"></param>
        /// <param name="ServiceEntityName"></param>
        /// <param name="method"></param>
        /// <param name="envirType"></param>
        /// <param name="serviceUrl"></param>
        /// <param name="resourceId"></param>
        /// <returns></returns>
        public bool Initialize(string serviceRoute, string ServiceEntityName, string method, EnvironmentType envirType,
            string serviceUrl, object resourceId, out string errorReason)
        {
            /*
            //5分钟过期，被动更新
            //不可靠的同步机制，有并发错误的风险，刚启动EsbHost应用的时候发生
            if ((AllServices == null || LastUpdateTime.AddMinutes(5) < DateTime.Now) && !IsFetchingDb)
            {
                IsFetchingDb = true;
                ServiceFactory factory = new ServiceFactory();
                var AllServices2 = factory.GetAllSvcs();
                var AllServices3 = new Dictionary<string, ServiceDo>();
                foreach (ServiceDo svc in AllServices2)
                {
                    AllServices3.Add(svc.ServiceRoute, svc);
                }
                AllServices = AllServices3;
                LastUpdateTime = DateTime.Now;
                IsFetchingDb = false;
            }
            this.serviceDo = AllServices[serviceRoute];
            //从内存中获得项

            //避免前面的风险，如果真的没有数据，再从数据库查一遍
            if (this.serviceDo == null)
            {
                ServiceFactory factory = new ServiceFactory();
                this.serviceDo = factory.GetServiceByServiceName(serviceRoute);//Db拿数据
                AllServices.Add(this.serviceDo.ServiceRoute, this.serviceDo);//可能加重复
            }
             */

            errorReason = "";

            this.Method = method;

            ServiceFactory factory = new ServiceFactory();
            this.Service = factory.GetServiceByServiceName(serviceRoute);

            if (this.Service == null)
            {
                errorReason = "找不到此名称的服务";
                return false;
            }
            else
            {
                //匹配该环境的服务地址，如果不存在，返回false
                EnvironmentDo envir = this.Service.Environments.FirstOrDefault(e => e.EnvironmentType == envirType);
                if (envir != null)
                {
                    //TODO:消费应用访问权限控制
                    if (true)// && this.serviceDo.Consumers.Contains(this.ConsumerId))
                    {
                        //基础url，支持客户端传入的显示url配置
                        string baseUrl = envir.GetServiceUrl();
                        if(!string.IsNullOrEmpty(serviceUrl))
                        {
                            baseUrl = serviceUrl;
                        }

                        if (!string.IsNullOrEmpty(ServiceEntityName))
                        {
                            this.ServiceUrl = baseUrl + "/" + ServiceEntityName;
                            //如果带有服务名，则加上
                        }
                        else
                        {
                            this.ServiceUrl = baseUrl;
                        }

                        if(resourceId != null)
                        {
                            this.ServiceUrl += "/" + resourceId.ToString();//增加对资源Id的支持
                        }

                        this.isReady = true;
                        this.Arch = this.Service.Architecture;//配置架构配置
                        return true;
                    }
                    else
                    {
                        //返回信息，此消费者不具有相应权限，返回不可调用的具体的原因
                        errorReason = "消费应用对此服务没有权限";
                    }
                }

                errorReason = "未找到此服务此环境的配置";
                return false;
            }
        }

        /// <summary>
        /// 使用报文调用服务
        /// </summary>
        /// <param name="requestBody"></param>
        /// <param name="methodName"></param>
        /// <returns></returns>
        public string Call(string requestBody)
        {
            if (!this.isReady)
                throw new Exception("服务有异常，尚未准备好调用");

            //判断协议格式是否要转换，此处不适合转换，直接在客户端配置好格式
            //requestBody = ContractTranslator.FromXMLToJson(requestBody);//格式转换

            string responseBody = this.ServiceHandler.ProcessCallService(requestBody, this.ServiceUrl, this.Method);

            //判断协议格式是否要转换
            //responseBody = ContractTranslator.FromJsonToXML(responseBody);
            return responseBody;
        }
    }
}
